﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
 
namespace Solidi
{
	public sealed class DependencyConstructor
	{
		private Func<object[], object> _factory;
		private DependencyConstructorParameter[] _parameters;

		public DependencyConstructor(ConstructorInfo constructor)
		{
			_parameters = constructor.GetParameters()
				.Select(parameter => new DependencyConstructorParameter(parameter))
				.ToArray();

			_factory = CreateFactory(constructor);
		}

		public object Create(object[] parameterValues)
		{
			return _factory(parameterValues);
		}

		public DependencyConstructorParameter[] Parameters
		{
			get
			{
				return _parameters;
			}
		}

		private Func<object[], object> CreateFactory(ConstructorInfo constructorInfo)
		{
			ParameterExpression argumentExpression = Expression.Parameter(typeof(object[]), "parameters");

			Expression[] parameterExpressions = ParameterExpressionEnumerator(argumentExpression).ToArray();

			Expression newExpression = Expression.New(constructorInfo, parameterExpressions);

			return Expression.Lambda<Func<object[], object>>(newExpression, new[] { argumentExpression }).Compile();
		}

		private IEnumerable<Expression> ParameterExpressionEnumerator(ParameterExpression argumentExpression)
		{
			return _parameters.Select((parameter, index) =>
			{
				Expression indexExpression = Expression.ArrayIndex(argumentExpression, Expression.Constant(index, typeof(int)));

				Type parameterType = _parameters[index].ParameterType.IsByRef ? _parameters[index].ParameterType.GetElementType() : _parameters[index].ParameterType;

				return Expression.Convert(indexExpression, parameterType);
			});
		}
	}
}
